package ezy.widget.listview;

import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.util.AttributeSet;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;

import ezy.widget.R;


public class FlowListView extends ViewGroup {
    private static final int[] R_styleable_FlowListView = {
            android.R.attr.horizontalSpacing,
            android.R.attr.verticalSpacing,
            R.attr.cols,
            R.attr.rows
    };
    private static final int FlowListView_horizontalSpacing = 0;
    private static final int FlowListView_verticalSpacing = 1;
    private static final int FlowListView_cols = 2;
    private static final int FlowListView_rows = 3;


    private int _horizontalSpacing = 0;
    private int _verticalSpacing = 0;
	private int _cols = 0;
    private int _rows = 99999;

    private ListAdapter _adapter;
    private View _emptyView;
    private OnItemClickListener _onItemClickListener;
    private DataSetObserver _observer = new DataSetObserver() {
        @Override
        public void onChanged() {
            setupChildren();
            requestLayout();
        }
    };

	public FlowListView(Context context) {
		super(context);
		this.readStyleParameters(context, null);
	}

	public FlowListView(Context context, AttributeSet attributeSet) {
		super(context, attributeSet);
		this.readStyleParameters(context, attributeSet);
	}

	public FlowListView(Context context, AttributeSet attributeSet, int defStyle) {
		super(context, attributeSet, defStyle);
		this.readStyleParameters(context, attributeSet);
	}

    public ListAdapter getAdapter() {
        return _adapter;
    }
    public void setAdapter(ListAdapter adapter) {
        if (_adapter != null) {
            _adapter.unregisterDataSetObserver(_observer);
        }
        this._adapter = adapter;
        if (_adapter != null) {
            _adapter.registerDataSetObserver(_observer);
        }
        setupChildren();
        requestLayout();
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        _onItemClickListener = listener;
    }
    public final OnItemClickListener getOnItemClickListener() {
        return _onItemClickListener;
    }

    public void setEmptyView(View emptyView) {
        _emptyView = emptyView;

        final ListAdapter adapter = getAdapter();
        final boolean empty = ((adapter == null) || adapter.isEmpty());
        updateEmptyStatus(empty);
    }
    public View getEmptyView() {
        return _emptyView;
    }



    private void setupChildren() {
        updateEmptyStatus((_adapter == null) || _adapter.isEmpty());
        if (_adapter == null) {
            return;
        }
        int count = _adapter.getCount();
        for (int i = 0; i < count; i++) {
            View child = _adapter.getView(i, getChildAt(i), this);
            if (getChildAt(i) != child) {
                final int position = i;
                child.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        performItemClickInternal(view, position);
                    }
                });
                addViewInLayout(child, i, child.getLayoutParams(), true);
            } else {
                child.setLayoutParams(generateDefaultLayoutParams());
            }
        }
        if (count < getChildCount()) {
            removeViewsInLayout(count, getChildCount() - count);
        }
    }

    private void updateEmptyStatus(boolean empty) {
        if (empty) {
            if (_emptyView != null) {
                _emptyView.setVisibility(View.VISIBLE);
                setVisibility(View.GONE);
            } else {
                setVisibility(View.VISIBLE);
            }
        } else {
            if (_emptyView != null) {
                _emptyView.setVisibility(View.GONE);
            }
            setVisibility(View.VISIBLE);
        }
    }

    private void readStyleParameters(Context context, AttributeSet attributeSet) {
        TypedArray a = context.obtainStyledAttributes(attributeSet, R_styleable_FlowListView);
        try {
            _horizontalSpacing = a.getDimensionPixelSize(FlowListView_horizontalSpacing, _horizontalSpacing);
            _verticalSpacing = a.getDimensionPixelSize(FlowListView_verticalSpacing, _verticalSpacing);
            _cols = a.getInteger(FlowListView_cols, _cols);
            _rows = a.getInteger(FlowListView_rows, _rows);
        } finally {
            a.recycle();
        }
    }

    private boolean performItemClickInternal(View view, int position) {
        if ((_onItemClickListener != null) && (_adapter != null)) {
            playSoundEffect(SoundEffectConstants.CLICK);
            _onItemClickListener.onItemClick(this, view, position, _adapter.getItemId(position));
            return true;
        }
        return false;
    }

	@Override
	protected void onMeasure(int widthSpec, int heightSpec) {
		int sizeWidth = MeasureSpec.getSize(widthSpec) - this.getPaddingRight() - this.getPaddingLeft();
		int modeWidth = MeasureSpec.getMode(widthSpec);
        int hPadding = this.getPaddingLeft() + this.getPaddingRight();
        int vPadding = this.getPaddingTop() + this.getPaddingBottom();

		int lineTop = 0, lineLeft = 0, lineMaxHeight = 0, lineMaxWidth = 0;

		int col = 1, row = 1;

        final int count = getChildCount();
		for (int i = 0; i < count; i++) {
			final View child = getChildAt(i);

            if (row > _rows) {
                child.setVisibility(View.GONE);
                continue;
            }
            child.setVisibility(View.VISIBLE);
			LayoutParams lp = (LayoutParams) child.getLayoutParams();
			child.measure(getChildMeasureSpec(widthSpec, hPadding, lp.width), getChildMeasureSpec(heightSpec, vPadding, lp.height));

            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();

			boolean newLine = false;
			if (_cols == 0) {
                if (lineLeft != 0 && modeWidth != MeasureSpec.UNSPECIFIED) {
                    newLine = (lineLeft + childWidth) > sizeWidth;
                }
			} else {
                if (col > _cols){
                    newLine = true;
                    col = 1;
                }
                col++;
            }

			if (newLine) {
                lineTop += lineMaxHeight + _verticalSpacing;
                lineMaxWidth = Math.max(lineMaxWidth, lineLeft - _horizontalSpacing);
				lineMaxHeight = 0;
				lineLeft = 0;

                row++;
                if (row > _rows) {
                    child.setVisibility(View.GONE);
                    continue;
                }
			}

			lp.x = getPaddingLeft() + lineLeft;
			lp.y = getPaddingTop() + lineTop;

            lineLeft += childWidth + _horizontalSpacing;
            lineMaxHeight = Math.max(lineMaxHeight, childHeight);
		}

        int width = hPadding + Math.max(lineMaxWidth, lineLeft - _horizontalSpacing);
        int height = vPadding + lineTop + lineMaxHeight;

		this.setMeasuredDimension(resolveSize(width, widthSpec), resolveSize(height, heightSpec));
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		final int count = getChildCount();
		for (int i = 0; i < count; i++) {
			View child = getChildAt(i);
            if (child.getVisibility() == View.VISIBLE) {
                LayoutParams lp = (LayoutParams) child.getLayoutParams();
                child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child.getMeasuredHeight());
            }
		}
	}

	@Override
	protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
		return p instanceof LayoutParams;
	}

	@Override
	protected LayoutParams generateDefaultLayoutParams() {
		return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
	}

	@Override
	public LayoutParams generateLayoutParams(AttributeSet attributeSet) {
		return new LayoutParams(getContext(), attributeSet);
	}

	@Override
	protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
		return new LayoutParams(p);
	}

    public static interface OnItemClickListener {
        void onItemClick(FlowListView parent, View view, int position, long id);
    }

	public static class LayoutParams extends MarginLayoutParams {
		private int x;
		private int y;

		public LayoutParams(Context context, AttributeSet attributeSet) {
			super(context, attributeSet);
		}

		public LayoutParams(int width, int height) {
			super(width, height);
		}

		public LayoutParams(ViewGroup.LayoutParams layoutParams) {
			super(layoutParams);
		}
	}

}